/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


/*
 * --------------------------------------------------------------------
 * jQuery inputToButton plugin
 * Author: Scott Jehl, scott@filamentgroup.com
 * Copyright (c) 2009 Filament Group
 * licensed under MIT (filamentgroup.com/examples/mit-license.txt)
 * --------------------------------------------------------------------
*/
(function($) {
    $.fn.visualize = function(options, container){
        return $(this).each(function(){
            //configuration
            var o = $.extend({
                type: 'bar', //also available: area, pie, line
                width: $(this).width(), //height of canvas - defaults to table height
                height: $(this).height(), //height of canvas - defaults to table height
                appendTitle: true, //table caption text is added to chart
                title: null, //grabs from table caption if null
                appendKey: true, //color key is added to chart
                colors: ['#be1e2d','#666699','#92d5ea','#ee8310','#8d10ee','#5a3b16','#26a4ed','#f45a90','#e9e744'],
                textColors: [], //corresponds with colors array. null/undefined items will fall back to CSS
                parseDirection: 'x', //which direction to parse the table data
                pieMargin: 20, //pie charts only - spacing around pie
                pieLabelPos: 'inside',
                lineWeight: 4, //for line and area - stroke weight
                barGroupMargin: 10,
                barMargin: 1, //space around bars in bar chart (added to both sides of bar)
                yLabelInterval: 30 //distance between y labels
            },options);

            //reset width, height to numbers
            o.width = parseFloat(o.width);
            o.height = parseFloat(o.height);


            var self = $(this);

            //function to scrape data from html table
            function scrapeTable(){
                var colors = o.colors;
                var textColors = o.textColors;
                var tableData = {
                    dataGroups: function(){
                        var dataGroups = [];
                        if(o.parseDirection == 'x'){
                            self.find('tr:gt(0)').each(function(i){
                                dataGroups[i] = {};
                                dataGroups[i].points = [];
                                dataGroups[i].color = colors[i];
                                if(textColors[i]){
                                    dataGroups[i].textColor = textColors[i];
                                }
                                $(this).find('td').each(function(){
                                    dataGroups[i].points.push( parseFloat($(this).text()) );
                                });
                            });
                        }
                        else {
                            var cols = self.find('tr:eq(1) td').size();
                            for(var i=0; i<cols; i++){
                                dataGroups[i] = {};
                                dataGroups[i].points = [];
                                dataGroups[i].color = colors[i];
                                if(textColors[i]){
                                    dataGroups[i].textColor = textColors[i];
                                }
                                self.find('tr:gt(0)').each(function(){
                                    dataGroups[i].points.push( $(this).find('td').eq(i).text()*1 );
                                });
                            };
                        }
                        return dataGroups;
                    },
                    allData: function(){
                        var allData = [];
                        $(this.dataGroups()).each(function(){
                            allData.push(this.points);
                        });
                        return allData;
                    },
                    dataSum: function(){
                        var dataSum = 0;
                        var allData = this.allData().join(',').split(',');
                        $(allData).each(function(){
                            dataSum += parseFloat(this);
                        });
                        return dataSum
                    },
                    topValue: function(){
                        var topValue = 0;
                        var allData = this.allData().join(',').split(',');
                        $(allData).each(function(){
                            if(parseFloat(this,10)>topValue) topValue = parseFloat(this);
                        });
                        return topValue;
                    },
                    bottomValue: function(){
                        var bottomValue = 0;
                        var allData = this.allData().join(',').split(',');
                        $(allData).each(function(){
                            if(this<bottomValue) bottomValue = parseFloat(this);
                        });
                        return bottomValue;
                    },
                    memberTotals: function(){
                        var memberTotals = [];
                        var dataGroups = this.dataGroups();
                        $(dataGroups).each(function(l){
                            var count = 0;
                            $(dataGroups[l].points).each(function(m){
                                count +=dataGroups[l].points[m];
                            });
                            memberTotals.push(count);
                        });
                        return memberTotals;
                    },
                    yTotals: function(){
                        var yTotals = [];
                        var dataGroups = this.dataGroups();
                        var loopLength = this.xLabels().length;
                        for(var i = 0; i<loopLength; i++){
                            yTotals[i] =[];
                            var thisTotal = 0;
                            $(dataGroups).each(function(l){
                                yTotals[i].push(this.points[i]);
                            });
                            yTotals[i].join(',').split(',');
                            $(yTotals[i]).each(function(){
                                thisTotal += parseFloat(this);
                            });
                            yTotals[i] = thisTotal;

                        }
                        return yTotals;
                    },
                    topYtotal: function(){
                        var topYtotal = 0;
                        var yTotals = this.yTotals().join(',').split(',');
                        $(yTotals).each(function(){
                            if(parseFloat(this,10)>topYtotal) topYtotal = parseFloat(this);
                        });
                        return topYtotal;
                    },
                    totalYRange: function(){
                        return this.topValue() - this.bottomValue();
                    },
                    xLabels: function(){
                        var xLabels = [];
                        if(o.parseDirection == 'x'){
                            self.find('tr:eq(0) th').each(function(){
                                xLabels.push($(this).html());
                            });
                        }
                        else {
                            self.find('tr:gt(0) th').each(function(){
                                xLabels.push($(this).html());
                            });
                        }
                        return xLabels;
                    },
                    yLabels: function(){
                        var yLabels = [];
                        yLabels.push(bottomValue);
                        var numLabels = Math.round(o.height / o.yLabelInterval);
                        var loopInterval = Math.ceil(totalYRange / numLabels) || 1;
                        while( yLabels[yLabels.length-1] < topValue - loopInterval){
                            yLabels.push(yLabels[yLabels.length-1] + loopInterval);
                        }
                        yLabels.push(topValue);
                        return yLabels;
                    }
                };

                return tableData;
            };


            //function to create a chart
            var createChart = {
                pie: function(){

                    canvasContain.addClass('visualize-pie');

                    if(o.pieLabelPos == 'outside'){
                        canvasContain.addClass('visualize-pie-outside');
                    }

                    var centerx = Math.round(canvas.width()/2);
                    var centery = Math.round(canvas.height()/2);
                    var radius = centery - o.pieMargin;
                    var counter = 0.0;
                    var toRad = function(integer){
                        return (Math.PI/180)*integer;
                    };
                    var labels = $('<ul class="visualize-labels"></ul>')
                    .insertAfter(canvas);

                    //draw the pie pieces
                    $.each(memberTotals, function(i){
                        var fraction = (this <= 0 || isNaN(this))? 0 : this / dataSum;
                        ctx.beginPath();
                        ctx.moveTo(centerx, centery);
                        ctx.arc(centerx, centery, radius,
                            counter * Math.PI * 2 - Math.PI * 0.5,
                            (counter + fraction) * Math.PI * 2 - Math.PI * 0.5,
                            false);
                        ctx.lineTo(centerx, centery);
                        ctx.closePath();
                        ctx.fillStyle = dataGroups[i].color;
                        ctx.fill();
                        // draw labels
                        var sliceMiddle = (counter + fraction/2);
                        var distance = o.pieLabelPos == 'inside' ? radius/1.5 : radius +  radius / 5;
                        var labelx = Math.round(centerx + Math.sin(sliceMiddle * Math.PI * 2) * (distance));
                        var labely = Math.round(centery - Math.cos(sliceMiddle * Math.PI * 2) * (distance));
                        var leftRight = (labelx > centerx) ? 'right' : 'left';
                        var topBottom = (labely > centery) ? 'bottom' : 'top';
                        var percentage = Math.round(fraction*100);
                        if(percentage){
                            var labeltext = $('<span class="visualize-label">' + percentage + '%</span>')
                            .css(leftRight, 0)
                            .css(topBottom, 0);
                            if(labeltext)
                                var label = $('<li class="visualize-label-pos"></li>')
                                .appendTo(labels)
                                .css({
                                    left: labelx,
                                    top: labely
                                })
                                .append(labeltext);
                            labeltext
                            .css('margin-'+leftRight, -labeltext.width()/2)
                            .css('margin-'+topBottom, -labeltext.outerHeight()/2);


                            if(dataGroups[i].textColor){
                                labeltext.css('color', dataGroups[i].textColor);
                            }
                        }
                        counter+=fraction;
                    });
                },

                line: function(area){

                    if(area){
                        canvasContain.addClass('visualize-area');
                    }
                    else{
                        canvasContain.addClass('visualize-line');
                    }

                    //write X labels
                    var xInterval = canvas.width() / (xLabels.length -1);
                    var xlabelsUL = $('<ul class="visualize-labels-x"></ul>')
                    .width(canvas.width())
                    .height(canvas.height())
                    .insertBefore(canvas);
                    $.each(xLabels, function(i){
                        var thisLi = $('<li><span>'+this+'</span></li>')
                        .prepend('<span class="line" />')
                        .css('left', xInterval * i)
                        .appendTo(xlabelsUL);
                        var label = thisLi.find('span:not(.line)');
                        var leftOffset = label.width()/-2;
                        if(i == 0){
                            leftOffset = 0;
                        }
                        else if(i== xLabels.length-1){
                            leftOffset = -label.width();
                        }
                        label
                        .css('margin-left', leftOffset)
                        .addClass('label');
                    });

                    //write Y labels
                    var yScale = canvas.height() / totalYRange;
                    var liBottom = canvas.height() / (yLabels.length-1);
                    var ylabelsUL = $('<ul class="visualize-labels-y"></ul>')
                    .width(canvas.width())
                    .height(canvas.height())
                    .insertBefore(canvas);

                    $.each(yLabels, function(i){
                        var thisLi = $('<li><span>'+this+'</span></li>')
                        .prepend('<span class="line"  />')
                        .css('bottom',liBottom*i)
                        .prependTo(ylabelsUL);
                        var label = thisLi.find('span:not(.line)');
                        var topOffset = label.height()/-2;
                        if(i == 0){
                            topOffset = -label.height();
                        }
                        else if(i== yLabels.length-1){
                            topOffset = 0;
                        }
                        label
                        .css('margin-top', topOffset)
                        .addClass('label');
                    });

                    //start from the bottom left
                    ctx.translate(0,zeroLoc);
                    //iterate and draw
                    $.each(dataGroups,function(h){
                        ctx.beginPath();
                        ctx.lineWidth = o.lineWeight;
                        ctx.lineJoin = 'round';
                        var points = this.points;
                        var integer = 0;
                        ctx.moveTo(0,-(points[0]*yScale));
                        $.each(points, function(){
                            ctx.lineTo(integer,-(this*yScale));
                            integer+=xInterval;
                        });
                        ctx.strokeStyle = this.color;
                        ctx.stroke();
                        if(area){
                            ctx.lineTo(integer,0);
                            ctx.lineTo(0,0);
                            ctx.closePath();
                            ctx.fillStyle = this.color;
                            ctx.globalAlpha = .3;
                            ctx.fill();
                            ctx.globalAlpha = 1.0;
                        }
                        else {
                            ctx.closePath();
                        }
                    });
                },

                area: function(){
                    createChart.line(true);
                },

                bar: function(){

                    canvasContain.addClass('visualize-bar');

                    //write X labels
                    var xInterval = canvas.width() / (xLabels.length);
                    var xlabelsUL = $('<ul class="visualize-labels-x"></ul>')
                    .width(canvas.width())
                    .height(canvas.height())
                    .insertBefore(canvas);
                    $.each(xLabels, function(i){
                        var thisLi = $('<li><span class="label">'+this+'</span></li>')
                        .prepend('<span class="line" />')
                        .css('left', xInterval * i)
                        .width(xInterval)
                        .appendTo(xlabelsUL);
                        var label = thisLi.find('span.label');
                        label.addClass('label');
                    });

                    //write Y labels
                    var yScale = canvas.height() / totalYRange;
                    var liBottom = canvas.height() / (yLabels.length-1);
                    var ylabelsUL = $('<ul class="visualize-labels-y"></ul>')
                    .width(canvas.width())
                    .height(canvas.height())
                    .insertBefore(canvas);
                    $.each(yLabels, function(i){
                        var thisLi = $('<li><span>'+this+'</span></li>')
                        .prepend('<span class="line"  />')
                        .css('bottom',liBottom*i)
                        .prependTo(ylabelsUL);
                        var label = thisLi.find('span:not(.line)');
                        var topOffset = label.height()/-2;
                        if(i == 0){
                            topOffset = -label.height();
                        }
                        else if(i== yLabels.length-1){
                            topOffset = 0;
                        }
                        label
                        .css('margin-top', topOffset)
                        .addClass('label');
                    });

                    //start from the bottom left
                    ctx.translate(0,zeroLoc);
                    //iterate and draw
                    for(var h=0; h<dataGroups.length; h++){
                        ctx.beginPath();
                        var linewidth = (xInterval-o.barGroupMargin*2) / dataGroups.length; //removed +1
                        var strokeWidth = linewidth - (o.barMargin*2);
                        ctx.lineWidth = strokeWidth;
                        var points = dataGroups[h].points;
                        var integer = 0;
                        for(var i=0; i<points.length; i++){
                            var xVal = (integer-o.barGroupMargin)+(h*linewidth)+linewidth/2;
                            xVal += o.barGroupMargin*2;

                            ctx.moveTo(xVal, 0);
                            ctx.lineTo(xVal, Math.round(-points[i]*yScale));
                            integer+=xInterval;
                        }
                        ctx.strokeStyle = dataGroups[h].color;
                        ctx.stroke();
                        ctx.closePath();
                    }
                }
            };

            //create new canvas, set w&h attrs (not inline styles)
            var canvasNode = document.createElement("canvas");
            canvasNode.setAttribute('height',o.height);
            canvasNode.setAttribute('width',o.width);
            var canvas = $(canvasNode);

            //get title for chart
            var title = o.title || self.find('caption').text();

            //create canvas wrapper div, set inline w&h, append
            var canvasContain = (container || $('<div class="visualize" role="img" aria-label="Chart representing data from the table: '+ title +'" />'))
            .height(o.height)
            .width(o.width)
            .append(canvas);

            //scrape table (this should be cleaned up into an obj)
            var tableData = scrapeTable();
            var dataGroups = tableData.dataGroups();
            var allData = tableData.allData();
            var dataSum = tableData.dataSum();
            var topValue = tableData.topValue();
            var bottomValue = tableData.bottomValue();
            var memberTotals = tableData.memberTotals();
            var totalYRange = tableData.totalYRange();
            var zeroLoc = o.height * (topValue/totalYRange);
            var xLabels = tableData.xLabels();
            var yLabels = tableData.yLabels();

            //title/key container
            if(o.appendTitle || o.appendKey){
                var infoContain = $('<div class="visualize-info"></div>')
                .appendTo(canvasContain);
            }

            //append title
            if(o.appendTitle){
                $('<div class="visualize-title">'+ title +'</div>').appendTo(infoContain);
            }


            //append key
            if(o.appendKey){
                var newKey = $('<ul class="visualize-key"></ul>');
                var selector = (o.parseDirection == 'x') ? 'tr:gt(0) th' : 'tr:eq(0) th' ;
                self.find(selector).each(function(i){
                    $('<li><span class="visualize-key-color" style="background: '+dataGroups[i].color+'"></span><span class="visualize-key-label">'+ $(this).text() +'</span></li>')
                    .appendTo(newKey);
                });
                newKey.appendTo(infoContain);
            };

            //append new canvas to page

            if(!container){
                canvasContain.insertAfter(this);
            }
            if( typeof(G_vmlCanvasManager) != 'undefined' ){
                G_vmlCanvasManager.init();
                G_vmlCanvasManager.initElement(canvas[0]);
            }

            //set up the drawing board
            var ctx = canvas[0].getContext('2d');

            //create chart
            createChart[o.type]();

            //clean up some doubled lines that sit on top of canvas borders (done via JS due to IE)
            $('.visualize-line li:first-child span.line, .visualize-line li:last-child span.line, .visualize-area li:first-child span.line, .visualize-area li:last-child span.line, .visualize-bar li:first-child span.line,.visualize-bar .visualize-labels-y li:last-child span.line').css('border','none');
            if(!container){
                //add event for updating
                canvasContain.bind('visualizeRefresh', function(){
                    self.visualize(o, $(this).empty());
                });
            }
        }).next(); //returns canvas(es)
    };
})(jQuery);

